﻿#include "TimeManage.h"
#include <iostream>
#include <QTimer>
#include <ProcessInfoStartuct.h>
#include <qdebug.h>
#include <StdClassFun.h>
#include <CheckOptionAttributeSetting.h>
#include <QtConcurrent>
#include <ThreadCin.h>

SettingPage* TimeManage::settingInstance(const QString* file
	, QHash<QString, ProcessInfoStartuct*>* processMap
	, int* readResult) {
	static SettingPage setPage;
	if( file && processMap && readResult )
		*readResult = setPage.appSettingFile(*file, *processMap);
	return &setPage;
}

TimeManage::TimeManage(int& argc
	, char** argv
	, int i) : QApplication{ argc,argv,i } {
	parameter = initParameters(argc, argv, currentExeName);
	processRunMutex = new QMutex;
	xmlSet = new CheckOptionAttributeSetting("./xml/default.xml");
}

void TimeManage::creatorProcessInfoStartuctObj(ProcessInfoStartuct* processObj) {
	connect(processObj, &ProcessInfoStartuct::started, this, &TimeManage::started);
	connect(processObj, &ProcessInfoStartuct::finished, this, &TimeManage::finished);
	connect(processObj, &ProcessInfoStartuct::readyReadStandardOutput, this, &TimeManage::readyReadStandardOutput);
	connect(processObj, &ProcessInfoStartuct::stateChanged, this, &TimeManage::stateChanged);
	connect(processObj, &ProcessInfoStartuct::errorOccurred, this, &TimeManage::errorOccurred);
}

TimeManage::~TimeManage( ) {
	QHash<QString, ProcessInfoStartuct*>::iterator iterator = processMap.begin( );
	auto&& end = processMap.end( );
	for( ;iterator != end;++iterator )
		delete iterator.value( );
	auto timerMapIterator = timerMap.begin( );
	auto&& timerMapEnd = timerMap.end( );
	for( ;timerMapIterator != timerMapEnd;++timerMapIterator ) {
		//delete timerMapIterator.key( );
		delete timerMapIterator.value( );
	}
	auto&& size = timeDataMemory.size( );
	for( int index = 0;index < size;++index ) {
		QPair<QTime*, QTime*>* pair = timeDataMemory.at(index);
		delete pair->first;
		delete pair->second;
		delete pair;
	}
	size = processTimeIntervalMemory.size( );
	for( int index = 0;index < size;++index ) {
		delete processTimeIntervalMemory.at(index);
	}
	if( threadCin )
		delete threadCin;
	if( ioMutex )
		delete ioMutex;
	if( processRunMutex )
		delete processRunMutex;
	delete xmlSet;
}

int TimeManage::appendSettingFile(const QString& fileName) {
	int result;
	if( ! files.contains(fileName) )
		files.append(fileName);
	settingInstance(&fileName, &this->processMap, &result);
	return result;
}

void TimeManage::resetMaiProcessCallInfos( ) {
	// greater 时间点
	static QTime zeroMinTime(0, 0, 0, 0);
	// less 时间点
	static QTime zeroMaxTime(23, 59, 59, 999);
	auto iterator = processMap.begin( );
	auto&& end = processMap.end( );
	for( ;iterator != end;++iterator ) {
		ProcessInfoStartuct* processInfoStartuct = iterator.value( );
		processRun.append(processInfoStartuct);
		ProcessInfoStartuct::CallData* callData = processInfoStartuct->getCallData( );
		// 添加字段内存
		QTime* timeBuff = new QTime(0, 0, 0, 0);
		processTimeIntervalMemory.append(timeBuff);
		processTimeIntervalMap.insert(processInfoStartuct, timeBuff);
		// 设置时间段
		int timeComp = comppareTime(callData->Less, callData->Greater);
		if( timeComp < 0 ) {
			QTime* timeLess = new QTime(callData->Less);
			QTime* timeGreater = new QTime(callData->Greater);
			QPair<QTime*, QTime*>* pair = new QPair<QTime*, QTime*>(timeLess, timeGreater);
			timeDataMemory.append(pair);
			processTimeMap.insertMulti(processInfoStartuct, pair);
		} else {
			QTime* timeLessTop = new QTime(callData->Less);
			QTime* timeLessBotoom = new QTime(zeroMaxTime);
			QPair<QTime*, QTime*>* pair = new QPair<QTime*, QTime*>(timeLessTop, timeLessBotoom);
			timeDataMemory.append(pair);
			processTimeMap.insertMulti(processInfoStartuct, pair);
			QTime* timeGreaterTop = new QTime(callData->Greater);
			QTime* timeGreaterBotoom = new QTime(zeroMinTime);
			pair = new QPair<QTime*, QTime*>(timeGreaterTop, timeGreaterBotoom);
			timeDataMemory.append(pair);
			processTimeMap.insertMulti(processInfoStartuct, pair);
		}
		// 链接槽函数
		creatorProcessInfoStartuctObj(processInfoStartuct);
		// 获取最小调用间隔
		minTimeOut = callData->timeOut;
		qint64 seepTime = getSeepTime(minTimeOut, zeroMinTime);
		if( seepTime < minCallMs || minCallMs < 1 ) {
			minCallMs = seepTime;
			QTimer* buffTimer = new QTimer;
			connect(buffTimer, &QTimer::timeout, this, &TimeManage::processFirstStart);
			buffTimer->setSingleShot(true);
			timerMap.insert(buffTimer, processInfoStartuct);
			buffTimer->start(seepTime);
		}
	}
	// 开始获取配置
	defaultFileOption = xmlSet->readDefaultFile( );
	auto hash = defaultFileOption.data( );
	if( hash->contains(xmlSet->istreamNodeName) ) {
		auto sharedPointer = hash->value(xmlSet->istreamNodeName);
		QVector<QString>* data = sharedPointer.data( );
		ioMutex = new QMutex;
		threadCin = new ThreadCin(data, ioMutex);
		connect(threadCin, &ThreadCin::getCin, this, &TimeManage::getCin);
		threadCin->start( );
	}
	if( hash->contains(xmlSet->fileNodeName) ) {
		auto sharedPointer = hash->value(xmlSet->fileNodeName);
		QVector<QString>* data = sharedPointer.data( );
		writeFilesSemaphore.release(data->size( ));
	}
	///////////////////////////////////
	// 开始计时
	if( timer == Q_NULLPTR ) {
		timer = new QTimer;
		connect(timer, &QTimer::timeout, this, &TimeManage::TimerTimeOut);
	}
	timer->setSingleShot(false);
	if( startMinNsec < 0 || minCallMs == 0 ) {
		exit(-100);
	}
	if( minCallMs > 0 && minCallMs < startMinNsec )
		timer->start(minCallMs);
	else
		timer->start(startMinNsec);
}

int TimeManage::runMainProcess( ) {
	resetMaiProcessCallInfos( );
	return exec( );
}

void TimeManage::processFirstStart( ) {
	QTimer* qobjectCast = qobject_cast<QTimer*>(sender( ));
	if( qobjectCast == Q_NULLPTR )
		return;
	timerMap.value(qobjectCast)->start( );
	timerMap.remove(qobjectCast);
	delete qobjectCast;
}

void TimeManage::startRunProcess(QTime& currentDateTime
	, ProcessInfoStartuct* processInfoStartuct
	, QTime* time
	, int& seepTime
	, QMutexLocker& locker) {
	auto&& process = processInfoStartuct->getProcess( );
	if( process ) {
		if( process->state( ) != QProcess::Running ) {
			if( time != Q_NULLPTR ) {
				*time = time->addMSecs(seepTime);
				int start = processInfoStartuct->start( );
				if( start == 0 ) {
					locker.relock( );
					processRun.removeOne(processInfoStartuct);
					locker.unlock( );
				}
			}
		}
	} else {
		if( time != Q_NULLPTR ) {
			*time = time->addMSecs(seepTime);
			int start = processInfoStartuct->start( );
			if( start == 0 ) {
				locker.relock( );
				processRun.removeOne(processInfoStartuct);
				locker.unlock( );
			}
		}
	}
}

void TimeManage::TimerTimeOut( ) {
	// 检查失效进程
	auto processError = procesProcessErrorMap.begin( );
	auto&& error = procesProcessErrorMap.end( );
	for( ;processError != error;++processError ) {
		const QString key = processMap.key(processError.key( ), "null");
		if( processMap.contains(key) ) {
			processMap.remove(key);
		} else {
			std::cout << QString("\n\n===\n移除\t错误进程调用配置\t" + processError.key( )->getProcessAbsolteFilePath( ) + "\t失败").toLocal8Bit( ).constData( ) << "\n\n===\n\n" << std::endl;
		}
	}
	procesProcessErrorMap.clear( );
	// 输出状态
	auto&& currentDateTime = QTime::currentTime( );
	if( processMap.size( ) == 0 ) {
		std::cout << ( "当前时间\t" + currentDateTime.toString("HH:mm:ss.zzz\t") + "\t所有任务已经运行完毕\n\t" ).toLocal8Bit( ).constData( ) << std::endl;
		exit(0);
	}
	QMutexLocker locker(processRunMutex);
	if( processRun.size( ) == 0 )
		return;
	// 开始使用

	locker.unlock( );
	auto iterator = processMap.begin( );
	auto&& end = processMap.end( );
	for( ;iterator != end;++iterator ) {
		locker.relock( );
		ProcessInfoStartuct* processInfoStartuct = iterator.value( );
		if( !processRun.contains(processInfoStartuct) ) {
			locker.unlock( );
			continue;
		}
		locker.unlock( );
		auto&& callData = processInfoStartuct->getCallData( );
		// 获取上次时间
		QTime* time = processTimeIntervalMap.value(processInfoStartuct);
		// 比较上次时间
		qint64 seepTime = getSeepTime(currentDateTime, *time);
		auto&& toMsec = timeToMsec(callData->timeOut);
		if( toMsec > seepTime ) {
			std::cout << ( "当前时间\t" + currentDateTime.toString("HH:mm:ss.zzz\t") + "\t不会运行\n\t" + processInfoStartuct->getProcessAbsolteFilePath( ) + "\n\t程序\n\t\t时间未达到\n\t还差\t\t" ).toLocal8Bit( ).constData( ) << toMsec - seepTime << "\t ms" << std::endl;
			continue;
		}
		// 获取运行范围
		QList<QPair<QTime*, QTime*>*> pairs = processTimeMap.values(processInfoStartuct);
		auto&& pairsSize = pairs.size( );
		for( int index = 0;index < pairsSize;++index ) {
			QPair<QTime*, QTime*>* pair = pairs.at(index);
			if( comppareTime(currentDateTime, *pair->first) >= 0 )
				if( comppareTime(currentDateTime, *pair->second) <= 0 ) {
					int buffSeepTime = seepTime;
					startRunProcess(currentDateTime, processInfoStartuct, time, buffSeepTime, locker);
				}
		}
	}
}

const char* TimeManage::getState(QProcess::ProcessState newState) {
	static const char* notrunning = "NotRunning";
	static const char* starting = "Starting";
	static const char* Running = "Running";
	static const char* Nont = "Nont";
	if( newState == QProcess::NotRunning )
		return notrunning;
	if( newState == QProcess::Starting )
		return starting;
	if( newState == QProcess::Running )
		return Running;
	return Nont;
}

const char* TimeManage::getState(QProcess::ExitStatus exitStatus) {
	static const char* NormalExit = "NormalExit";
	static const char* CrashExit = "CrashExit";
	static const char* Nont = "Nont";
	if( exitStatus == QProcess::ExitStatus::NormalExit )
		return NormalExit;
	if( exitStatus == QProcess::ExitStatus::CrashExit )
		return CrashExit;
	return Nont;
}

const char* TimeManage::getState(QProcess::ProcessError error) {
	static const char* FailedToStart = "FailedToStart";
	static const char* Crashed = "Crashed";
	static const char* Timedout = "Timedout";
	static const char* ReadError = "ReadError";
	static const char* WriteError = "WriteError";
	static const char* UnknownError = "UnknownError";
	static const char* Nont = "Nont";
	if( error == QProcess::ProcessError::FailedToStart )
		return FailedToStart;
	if( error == QProcess::ProcessError::Crashed )
		return Crashed;
	if( error == QProcess::ProcessError::Timedout )
		return Timedout;
	if( error == QProcess::ProcessError::ReadError )
		return ReadError;
	if( error == QProcess::ProcessError::WriteError )
		return WriteError;
	if( error == QProcess::ProcessError::UnknownError )
		return UnknownError;
	return Nont;
}

void TimeManage::finished(int exitCode
	, QProcess::ExitStatus exitStatus) {
	QTextCodec* codec = QTextCodec::codecForLocale( );
	ProcessInfoStartuct* instances = qobject_cast<ProcessInfoStartuct*>(sender( ));
	ProcessInfoStartuct::CallData* callData = instances->getCallData( );
	QByteArray byteArray = instances->getProcess( )->readAllStandardOutput( );
	std::cout << codec->toUnicode(byteArray).toLocal8Bit( ).constData( ) << std::endl;
	if( exitCode != 0 ) {
		qint32* reStart = &callData->reStart;
		std::cout << QString("\t\n" + instances->getProcessAbsolteFilePath( ) + "\n\t进程异常结束\t退出代码:\t").toLocal8Bit( ).constData( ) << exitCode << std::endl;
		if( *reStart > 0 ) {
			*reStart -= 1;
			QTimer* buffTimer = new QTimer;
			connect(buffTimer, &QTimer::timeout, this, &TimeManage::processFirstStart);
			buffTimer->setSingleShot(true);
			timerMap.insert(buffTimer, instances);
			qint64 msec = timeToMsec(callData->reStartTimeOut);
			std::cout << QString("\t\n" + instances->getProcessAbsolteFilePath( ) + "\n\t进程将会在:\t").toLocal8Bit( ).constData( ) << msec << QString("\t秒后重启").toLocal8Bit( ).constData( ) << std::endl;
			buffTimer->start(msec);
			return;
		}
	} else
		std::cout << QString("\t\n" + instances->getProcessAbsolteFilePath( ) + "\n\t进程结束").toLocal8Bit( ).constData( ) << std::endl;
	runProcess.remove(instances);
}

void TimeManage::readyReadStandardOutput( ) {
	static QTextCodec* codec = QTextCodec::codecForLocale( );
	ProcessInfoStartuct* instances = qobject_cast<ProcessInfoStartuct*>(sender( ));
	if( !runProcess.contains(instances) )
		return;
	QByteArray byteArray = instances->getProcess( )->readAllStandardOutput( );
	QString string = codec->toUnicode(byteArray);
	QHash<QString, QSharedPointer<QVector<QString>>>* hash = defaultFileOption.data( );
	if( hash->contains(xmlSet->coutNodeName) ) {
		QVector<QString>* charses = defaultFileOption.data( )->value(xmlSet->coutNodeName).data( );
		for( int index = 0, size = charses->size( );index < size;++index ) {
			bool contains = processMap.contains(charses->at(index));
			if( contains )
				std::cout << string.toLocal8Bit( ).constData( ) << std::endl;
			else {
				charses->removeAt(index);
				--size;
				--index;
			}
		}
	}

	// 从子进程产生输入
	const QSharedPointer<QVector<QString>> sharedPointer = hash->value(xmlSet->istreamNodeName);
	QVector<QString>* vector = sharedPointer.data( );
	for( int index = 0, size = vector->size( );index < size;++index ) {
		auto strings = vector->at(index);
		bool contains = processMap.contains(strings);
		if( contains )
			processMap.value(strings)->getProcess( )->write(( string + "\r\n" ).toLocal8Bit( ));
		else {
			vector->removeAt(index);
			--size;
			--index;
		}
	}

	// 子进程输入送入文件
	// 等待足够的信号
	auto fileNodes = hash->value(xmlSet->fileNodeName).data( );
	int n = fileNodes->size( );
	writeFilesSemaphore.acquire(n);
	for( int index = 0;index < n;++index ) {
		QtConcurrent::run(this, &TimeManage::writeFileInfo, fileNodes->operator[](index), string, &writeFilesSemaphore);
	}
}

void TimeManage::writeFileInfo(const QString filetarget
	, const QString writeCon
	, QSemaphore* qSemaphore) {
	QFile file(filetarget);
	if( !file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append) )
		std::cout << QString("文件打开失败\n").toLocal8Bit( ).constData( ) << std::endl;
	else {
		file.write(writeCon.toUtf8( ));
	}
	qSemaphore->release(1);
}

void TimeManage::started( ) {
	ProcessInfoStartuct* instances = qobject_cast<ProcessInfoStartuct*>(sender( ));
	runProcess.insert(instances, QProcess::ProcessState( ));
}

void TimeManage::stateChanged(QProcess::ProcessState newState) {
	ProcessInfoStartuct* instances = qobject_cast<ProcessInfoStartuct*>(sender( ));
	if( runProcess.contains(instances) )
		runProcess.insert(instances, newState);
}

void TimeManage::errorOccurred(QProcess::ProcessError error) {
	ProcessInfoStartuct* instances = qobject_cast<ProcessInfoStartuct*>(sender( ));
	std::cout << QString("\t\n" + instances->getProcessAbsolteFilePath( ) + "\n\t进程异常结束\t\t").toLocal8Bit( ).constData( ) << getState(error) << std::endl;
	runProcess.remove(instances);
	procesProcessErrorMap.insert(instances, error);
}

void TimeManage::getCin(QString* str
	, QSemaphore* qSemaphore) {
	QHash<QString, QSharedPointer<QVector<QString>>>* hash = defaultFileOption.data( );
	const QSharedPointer<QVector<QString>> sharedPointer = hash->value(xmlSet->istreamNodeName);
	QVector<QString>* vector = sharedPointer.data( );
	for( int index = 0, size = vector->size( );index < size;++index ) {
		auto strings = vector->at(index);
		bool contains = processMap.contains(strings);
		if( contains )
			processMap.value(strings)->getProcess( )->write(( *str + "\r\n" ).toLocal8Bit( ));
		else {
			vector->removeAt(index);
			--size;
			--index;
		}
		qSemaphore->release(1);
	}
}
